﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Dynamic;

namespace CRMTraceTool
{
    public class TraceSearch
    {
        public List<TraceRecord> queryTraceRecords(TraceFile tFile, string column, string queryOperator, string query)
        {
            List<TraceRecord> tRecord = new List<TraceRecord>();

            switch (queryOperator)
            {
                case "=":
                    tRecord = EqualsQuery(tFile, column, query);
                    break;
                case "Contains":
                    tRecord = ContainsQuery(tFile, column, query);
                    break;
                default:
                    tRecord = tFile.traceRecords;
                    break;
            }

            return tRecord;
        }

        private List<TraceRecord> EqualsQuery(TraceFile traceFile, string column, string searchItem)
        {
            string query = String.Empty;
            if (column == "RowId")
            {
                query = String.Format("{0} = {1}", column, searchItem);
            }
            else
                query = String.Format("{0} = \"{1}\"", column, searchItem);

            IQueryable<TraceRecord> traceRecord = traceFile.traceRecords.AsQueryable();

            traceRecord = traceRecord
                .Where(query);

            return traceRecord.ToList();
        }

        private List<TraceRecord> ContainsQuery(TraceFile traceFile, string column, string searchItem)
        {
            IQueryable<TraceRecord> traceRecord = traceFile.traceRecords.AsQueryable();
            string query = String.Empty;

            //var records = (dynamic)null;

            List<TraceRecord> tRecords = new List<TraceRecord>();
            
            switch (column)
            {
                case "RowId":
                    {
                        //var records = from tr in traceFile.traceRecords
                        //          where tr.RowId.Equals(searchItem)
                        //          select tr;
                        break;
                    }
                case "RecordTime":
                    {
                        tRecords = getRecordTimeContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Process":
                    {
                        tRecords = getRecordProcessContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Organization":
                    {
                        tRecords = getOrganizationContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Thread":
                    {
                        tRecords = getThreadContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Category":
                    {
                        tRecords = getCategoryContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "User":
                    {
                        tRecords = getUserContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Level":
                    {
                        tRecords = getLevelContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "ReqID":
                    {
                        tRecords = getReqIDContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Info":
                    {
                        tRecords = getInfoContains(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "CallStack":
                    {
                        tRecords = getCallStack(traceFile.traceRecords, searchItem);
                        break;
                    }
                case "Message":
                    {
                        tRecords = getMessage(traceFile.traceRecords, searchItem);
                        break;
                    }
            }

            return tRecords;
        }

        private List<TraceRecord> getMessage(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Message.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getCallStack(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.CallStack.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getInfoContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Info.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getReqIDContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.ReqID.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getLevelContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Level.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getUserContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.User.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getCategoryContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Category.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getThreadContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Thread.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getOrganizationContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Organization.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getRecordProcessContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.Process.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        private List<TraceRecord> getRecordTimeContains(List<TraceRecord> list, string searchItem)
        {
            IQueryable<TraceRecord> tRecord = list.AsQueryable();

            var records = from tr in tRecord
                          where tr.RecordTime.Contains(searchItem)
                          select tr;

            return records.ToList<TraceRecord>();
        }

        public List<users> ExtractUsersFromMessage(List<TraceRecord> tRecords)
        {
            List<users> extractedUsers = new List<users>(GetUsersFromTheMessage(tRecords));

            return GetUniqueUsers(extractedUsers);
        }

        public List<users> ExtractUsersFromColumns(List<TraceRecord> tRecords)
        {
            List<users> extractedUsers = new List<users>(GetUsersFromColumns(tRecords));

            return GetUniqueUsers(extractedUsers);
        }

        public List<users> ExtractUsersFromColumnAndMessages(List<TraceRecord> tRecords)
        {
            List<users> extractedUsers = new List<users>();
            List<users> extractedUsersFromColumns = new List<users>(GetUsersFromColumns(tRecords));
            List<users> extractedUsersFromMessages = new List<users>(GetUsersFromTheMessage(tRecords));

            foreach (users u in extractedUsersFromColumns)
            {
                extractedUsers.Add(u);
            }

            foreach (users u in extractedUsersFromMessages)
            {
                extractedUsers.Add(u);
            }

            return GetUniqueUsers(extractedUsers);
        }

        private List<users> GetUniqueUsers(List<users> extractedUsers)
        {
            int recordCount = 1;

            extractedUsers = extractedUsers
                .GroupBy(u => u.userId)
                .Select(g => g.First())
                .ToList();

            foreach (users u in extractedUsers)
            {
                u.rowId = recordCount;
                recordCount++;
            }

            return extractedUsers;
        }

        private List<users> GetUsersFromTheMessage(List<TraceRecord> tRecords)
        {
            List<users> extractedUsers = new List<users>();

            var messages = from tr in tRecords
                           where tr.Message.Contains("<UserID>&#123;")
                           select tr.Message;

            foreach (string message in messages)
            {
                string[] m = message.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
                foreach (string line in m)
                {
                    users user = new users();

                    if (line.StartsWith("    <OrgID>&#123;"))
                    {
                        user.orgId = line.Replace("    <OrgID>&#123;", "").Replace("&#125;</OrgID>", "");
                        extractedUsers.Add(user);
                    }

                    if (line.StartsWith("    <UserID>&#123;"))
                    {
                        extractedUsers[extractedUsers.Count - 1].userId = line.Replace("    <UserID>&#123;", "").Replace("&#125;</UserID>", "");
                    }
                }
            }

            return extractedUsers;
        }

        private List<users> GetUsersFromColumns(List<TraceRecord> tRecords)
        {
            List<users> extractedUsers = new List<users>();

            var users = from tr in tRecords
                        where tr.User != "d"
                        select new users()
                        {
                            orgId = tr.Organization,
                            userId = tr.User
                        };

            extractedUsers = users.ToList<users>();

            return extractedUsers;
        }

        public List<users> ExtractUser(List<TraceRecord> tRecords)  //old code
        {
            List<users> extractedUser = new List<users>();
            int recordCount = 1;

            var messages = from tr in tRecords
                           where tr.Message.Contains("<UserID>&#123;")
                           select tr.Message;

            foreach (string message in messages)
            {
                string[] m = message.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
                foreach (string line in m)
                {
                    users user = new users();

                    if (line.StartsWith("    <OrgID>&#123;"))
                    {
                        user.orgId = line.Replace("    <OrgID>&#123;", "").Replace("&#125;</OrgID>", "");
                        extractedUser.Add(user);
                    }

                    if (line.StartsWith("    <UserID>&#123;"))
                    {
                        extractedUser[extractedUser.Count - 1].userId = line.Replace("    <UserID>&#123;", "").Replace("&#125;</UserID>", "");
                    }
                }
            }

            extractedUser = extractedUser
                .GroupBy(u => u.userId)
                .Select(g => g.First())
                .ToList();

            recordCount = 1;
            foreach (users u in extractedUser)
            {
                u.rowId = recordCount;
                recordCount++;
            }

            return extractedUser;
        }

        public List<TraceRecord> SortColumn(TraceFile traceFile, string columnName, string sortDirection)
        {
            IQueryable<TraceRecord> traceRecord = traceFile.traceRecords.AsQueryable();

            switch (sortDirection.ToLower())
            {
                case "none":
                case "descending":
                    {
                        traceRecord = traceRecord
                            .OrderBy(String.Format("{0}", columnName));
                        break;
                    }
                case "ascending":
                    {
                        traceRecord = traceRecord
                            .OrderBy(String.Format("{0}", columnName)).Reverse();
                        break;
                    }
                default:
                    {
                        traceRecord = traceRecord
                            .OrderBy(String.Format("{0}", columnName));
                        break;
                    }
            }

            return traceRecord.ToList();
        }

        public string calculateTimeSpent(string Start, string Finish)
        {
            string timeDiff = String.Empty;

            try
            {
                DateTime startDate = new DateTime(
                    Convert.ToInt16(Start.Substring(0, 4)),         //year
                    Convert.ToInt16(Start.Substring(5, 2)),         //Month
                    Convert.ToInt16(Start.Substring(8, 2)),         //Day
                    Convert.ToInt16(Start.Substring(11, 2)),        //Hour
                    Convert.ToInt16(Start.Substring(14, 2)),        //Minute
                    Convert.ToInt16(Start.Substring(17, 2)),        //Second
                    Convert.ToInt16(Start.Substring(20, 3)));        //Millisecond

                DateTime finishDate = new DateTime(
                    Convert.ToInt16(Finish.Substring(0, 4)),         //year
                    Convert.ToInt16(Finish.Substring(5, 2)),         //Month
                    Convert.ToInt16(Finish.Substring(8, 2)),         //Day
                    Convert.ToInt16(Finish.Substring(11, 2)),        //Hour
                    Convert.ToInt16(Finish.Substring(14, 2)),        //Minute
                    Convert.ToInt16(Start.Substring(17, 2)),         //Second
                    Convert.ToInt16(Finish.Substring(20, 3)));        //Millisecond

                TimeSpan diff = finishDate.Subtract(startDate);

                timeDiff = diff.ToString();
            }
            catch
            {
            }

            return timeDiff;
        }

        public string ParseText(string parseMe)
        {
            #region HTMLCode
            Dictionary<string, string> htmlCode = new Dictionary<string, string>()
            {
                {"&#32;", " "},
                {"&#33;", "!"},
                {"&#34;", "\""},
                {"&quot;", "\""},
                {"&#35;", "#"},
                {"&#36;", "$"},
                {"&#37;", "%"},
                {"&#38;", "&"},
                {"&#39;", "'"},
                {"&#40;", "("},
                {"&#41;", ")"},
                {"&#42;", "*"},
                {"&#43;", "+"},
                {"&#44;", ","},
                {"&#45;", "-"},
                {"&#46;", "."},
                {"&#47;", "/"},
                {"&#48;", "0"},
                {"&#49;", "1"},
                {"&#50;", "2"},
                {"&#51;", "3"},
                {"&#52;", "4"},
                {"&#53;", "5"},
                {"&#54;", "6"},
                {"&#55;", "7"},
                {"&#56;", "8"},
                {"&#57;", "9"},
                {"&#58;", ":"},
                {"&#59;", ";"},
                {"&#60;", "<"},
                {"&lt;", "<"},
                {"&#61;", "="},
                {"&#62;", ">"},
                {"&gt;", ">"},
                {"&#63;", "?"},
                {"&#64;", "@"},
                {"&#65;", "A"},
                {"&#66;", "B"},
                {"&#67;", "C"},
                {"&#68;", "D"},
                {"&#69;", "E"},
                {"&#70;", "F"},
                {"&#71;", "G"},
                {"&#72;", "H"},
                {"&#73;", "I"},
                {"&#74;", "J"},
                {"&#75;", "K"},
                {"&#76;", "L"},
                {"&#77;", "M"},
                {"&#78;", "N"},
                {"&#79;", "O"},
                {"&#80;", "P"},
                {"&#81;", "Q"},
                {"&#82;", "R"},
                {"&#83;", "S"},
                {"&#84;", "T"},
                {"&#85;", "U"},
                {"&#86;", "V"},
                {"&#87;", "W"},
                {"&#88;", "X"},
                {"&#89;", "Y"},
                {"&#90;", "Z"},
                {"&#91;", "["},
                {"&#92;", "\\"},
                {"&#93;", "]"},
                {"&#94;", "^"},
                {"&#95;", "_"},
                {"&#96;", "`"},
                {"&#97;", "a"},
                {"&#98;", "b"},
                {"&#99;", "c"},
                {"&#100;", "d"},
                {"&#101;", "e"},
                {"&#102;", "f"},
                {"&#103;", "g"},
                {"&#104;", "h"},
                {"&#105;", "i"},
                {"&#106;", "j"},
                {"&#107;", "k"},
                {"&#108;", "l"},
                {"&#109;", "m"},
                {"&#110;", "n"},
                {"&#111;", "o"},
                {"&#112;", "p"},
                {"&#113;", "q"},
                {"&#114;", "r"},
                {"&#115;", "s"},
                {"&#116;", "t"},
                {"&#117;", "u"},
                {"&#118;", "v"},
                {"&#119;", "w"},
                {"&#120;", "x"},
                {"&#121;", "y"},
                {"&#122;", "z"},
                {"&#123;", "{"},
                {"&#124;", "|"},
                {"&#125;", "}"},
                {"&#126;", "~"}
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            //    //{"", ""},
            };
            #endregion

            StringBuilder p = new StringBuilder(parseMe);

            foreach (KeyValuePair<string, string> item in htmlCode)
            {
                p.Replace(item.Key, item.Value);
            }

            return p.ToString();
        }

        public StringBuilder generateCSV(List<TraceRecord> tRecords)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("RowId|");
            sb.Append("RecordTime|");
            sb.Append("Process|");
            sb.Append("Orgnization|");
            sb.Append("Thread|");
            sb.Append("Category|");
            sb.Append("User|");
            sb.Append("Level|");
            sb.Append("ReqID|");
            sb.Append("Info|");
            sb.Append("CallStack|");
            sb.Append("Message");
            sb.AppendLine();

            foreach (TraceRecord tr in tRecords)
            {
                sb.Append(tr.RowId);
                sb.Append("|");
                sb.Append(tr.RecordTime);
                sb.Append("|");
                sb.Append(tr.Process);
                sb.Append("|");
                sb.Append(tr.Organization);
                sb.Append("|");
                sb.Append(tr.Thread);
                sb.Append("|");
                sb.Append(tr.Category);
                sb.Append("|");
                sb.Append(tr.User);
                sb.Append("|");
                sb.Append(tr.Level);
                sb.Append("|");
                sb.Append(tr.ReqID);
                sb.Append("|");
                sb.Append(tr.Info);
                sb.Append("|");
                sb.Append(tr.CallStack);
                sb.Append("|");
                sb.Append(tr.Message);
                sb.AppendLine();
            }
            return sb;
        }
    }

    public class users
    {
        public int rowId { get; set; }
        public string orgId { get; set; }
        public string userId { get; set; }
    }

}